﻿package silin.geom
{
	import flash.display.Graphics;
	import flash.geom.Point;
	import silin.geom.path.Path;
	import silin.geom.path.PathPoint;
	
	/**
	 * утилиты рисования 
	 * @example
	 * <a href="http://www.silin.fatal.ru/AS3/geom/dash/index.html">silin.fatal.ru</a>
	 * @author silin
	 * 
	 */
	public class DrawUtils 
	{
		
		/**
		 * размер штриха
		 */
		public static var DASH_LENGTH:Number = 8;
		/**
		 * размер разрыва
		 */
		public static var GAP_LENGTH:Number = 4;
		
		/**
		 * рисует дугу в graphics
		 * @param	graphics		где рисовать
		 * @param	centerX			X-координата центра
		 * @param	centerY			Y-координата центра
		 * @param	radius			радиус
		 * @param	startAngle		начальный угол
		 * @param	arcAngle		угол дуги
		 * @param	steps			число отрезков
		 */
		public static function drawArc(graphics:Graphics, centerX:Number, centerY:Number, radius:Number, startAngle:Number, arcAngle:Number, steps:int=16):void
		{
			
			var angleStep:Number = arcAngle / steps;
			graphics.moveTo(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius);
			while(steps-->0)
			{
				startAngle+= angleStep
				graphics.lineTo(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius);
			}
		}
		/**
		 * рисует пунктирную дугу в graphics
		 * @param	graphics		где рисовать
		 * @param	centerX			X-координата центра
		 * @param	centerY			Y-координата центра
		 * @param	radius			радиус
		 * @param	startAngle		начальный угол
		 * @param	arcAngle		угол дуги
		 * @param	shift			начальная позиция штриха: 0..1 
		 */
		public static function drawDashedArc(graphics:Graphics, centerX:Number, centerY:Number, radius:Number, startAngle:Number, arcAngle:Number, shift:Number=0):void
		{
			var arcLen:Number = radius * Math.abs( arcAngle);
			var steps:int = Math.round(arcLen / (DASH_LENGTH + GAP_LENGTH));
			var angleStep:Number = arcAngle / steps;
			var dashStep:Number = DASH_LENGTH / (DASH_LENGTH + GAP_LENGTH) * angleStep;
			var gapStep:Number = angleStep - dashStep;
			
			startAngle += angleStep * shift;
			graphics.moveTo(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius);
			
			while(steps-->0)
			{
				startAngle += dashStep;
				graphics.lineTo(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius);
				startAngle += gapStep;
				graphics.moveTo(centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius);
			}
		}
		
		/**
		 * рисует пунктирную окружность в graphics
		 * @param	graphics		где рисовать
		 * @param	centerX			X-координата центра
		 * @param	centerY			Y-координата центра
		 * @param	radius			радиус
		 * @param	startAngle		начальный угол
		 * @param	arcAngle		угол дуги
		 * @param	shift			начальная позиция штриха, 0..1 
		 */
		public static function drawDashedCircle(graphics:Graphics, centerX:Number, centerY:Number, radius:Number, shift:Number=0):void
		{
			drawDashedArc(graphics, centerX, centerY, radius, 0, 2 * Math.PI, shift);
			
		}
		
		/**
		 * 
		 * @param	graphics		где рисовать
		 * @param	x				
		 * @param	y
		 * @param	width
		 * @param	height
		 * @param	shift			начальная позиция штриха, 0..1 
		 */
		public static function drawDashedRecangle(graphics:Graphics, x:Number, y:Number, width:Number, height:Number, shift:Number=0):void
		{
			var tl:Point = new Point(x, y);
			var tr:Point = new Point(x + width, y);
			var br:Point = new Point(x + width, y + height);
			var bl:Point = new Point(x, y + height);
			
			drawDashedLine(graphics, tl, tr, shift);
			drawDashedLine(graphics, tr, br, shift);
			drawDashedLine(graphics, br, bl, shift);
			drawDashedLine(graphics, bl, tl, shift);
		}
		
		/**
		 * рисует пунктирную линию в graphics
		 * @param	graphics		где рисовать
		 * @param	startPoint		начальная точка
		 * @param	endPoint		конечеая точка
		 * @param	shift			начальная позиция штриха, 0..1 
		 */
		public static function drawDashedLine(graphics:Graphics, startPoint:Point, endPoint:Point, shift:Number=0):void
		{
			shift %= 1;
			if (shift < 0) shift += 1;
			var dashMode:Boolean = false;
			//текущая длина в зависмости от того с чего начинаем: с черточки или пробела
			var currLen:Number = shift * (GAP_LENGTH + DASH_LENGTH);
			//общая длина
			var totLen:Number = Point.distance(startPoint, endPoint);
			
			var dir:Number = Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
			var cos:Number = Math.cos(dir);
			var sin:Number = Math.sin(dir);
			var cX:Number;
			var cY:Number;
			//если стратуем дальше промежутка, то надо дорисовать хвостик черточки в начале
			if (currLen > GAP_LENGTH)
			{
				
				graphics.moveTo(startPoint.x, startPoint.y);
				cX = startPoint.x + (currLen - GAP_LENGTH) * cos;
				cY = startPoint.y + (currLen - GAP_LENGTH) * sin;
				graphics.lineTo(cX, cY);
				
			}
			
			while (currLen < totLen)
			{
				//текущая точка
				cX = startPoint.x + currLen * cos;
				cY = startPoint.y + currLen * sin;
				//рисуем или двигаем указатель 
				if (dashMode)
				{
					graphics.lineTo(cX, cY);
					currLen += GAP_LENGTH;
				}else
				{
					graphics.moveTo(cX, cY);
					currLen += DASH_LENGTH;
				}
				dashMode = !dashMode;
			}
			//если закончилось на стадии линии, то дорисовываем остаток черточки
			if (dashMode) graphics.lineTo(endPoint.x, endPoint.y);
			
		}
		
		/**
		 * рисует пунктирную кривую в graphics
		 * @param	graphics		где рисовать
		 * @param	startPoint		начальная точка
		 * @param	controlPoint	управляющая точка
		 * @param	endPoint		конечная точка
		 * @param	shift			начальная позиция штриха, 0..1 
		 */
		public static function drawDashedCurve(graphics:Graphics, startPoint:Point, controlPoint:Point, endPoint:Point, shift:Number=0):void
		{
			drawDashedPath(graphics, [startPoint, controlPoint, endPoint], shift);
		}
		
		/**
		 * рисуеет пунктирную кривую, заданную массивом точек [(cX,cY),(aX,aY),..,(cX,cY)]
		 * @param	graphics		где рисовать
		 * @param	points			массив точек
		 * @param	shift			начальная позиция штриха, 0..1 
		 */
		public static function drawDashedPath(graphics:Graphics, points:Array, shift:Number=0):void
		{
			
			shift %= 1;
			if (shift < 0) shift += 1;
			
			var dashMode:Boolean = false;
			var path:Path = new Path(points);
			var currLen:Number =  shift * (GAP_LENGTH+DASH_LENGTH);
			var totLen:Number = path.pathLength;
			var startPoint:Point = points[0];
			var endPoint:Point = points[points.length - 1];
			var currPoint:PathPoint;
			//если стратуем дальше промежутка, то надо дорисовать хвостик черточки в начале
			if (currLen > GAP_LENGTH)
			{
				graphics.moveTo(startPoint.x, startPoint.y);
				currPoint = path.getPathPoint(currLen - GAP_LENGTH);
				graphics.lineTo(currPoint.x, currPoint.y);
			}
			
			while (currLen < totLen)
			{
				//текущая точка
				currPoint = path.getPathPoint(currLen);
				//рисуем или двигаем ло указателя, двигаем указатель 
				if (dashMode)
				{
					graphics.lineTo(currPoint.x, currPoint.y);
					currLen += GAP_LENGTH;
				}else
				{
					graphics.moveTo(currPoint.x, currPoint.y);
					currLen += DASH_LENGTH;
				}
				dashMode = !dashMode;
			}
			//если закончилось на стадии линии, то дорисовываем остаток черточки
			if (dashMode) graphics.lineTo(endPoint.x, endPoint.y);
			
		}
		/**
		 * рисует по точкам points сглаженную пунктирную линию
		 * @param	graphics	где рисовать
		 * @param	points		массив точек по которым сглаживать
		 * @param	shift		начальная позиция штриха, 0..1
		 * @param	coeff		коефф. сглаживания
		 * @param	fitMode		степень кривой подгонки (quadratic|cubic)
		 */
		public static function drawDashedFitLine(graphics:Graphics, points:Array, shift:Number=0,coeff:Number=0.38, fitMode:String="quadratic"):void
		{
			
			var fpath:FitLine = new FitLine(points, coeff, fitMode);
			drawDashedPath(graphics, fpath.fitPoints, shift);
			
		}
		/**
		 * рисует по точкам points сглаженную линию
		 * @param	graphics	где рисовать
		 * @param	points		массив точек по которым сглаживать
		 * @param	shift		начальная позиция штриха, 0..1
		 * @param	coeff		коефф. сглаживания
		 * @param	fitMode		степень кривой подгонки (quadratic|cubic)
		 */
		public static function drawFitLine(graphics:Graphics, points:Array, shift:Number = 0, coeff:Number = 0.38, fitMode:String = "quadratic"):void
		{
			var fpath:FitLine = new FitLine(points, coeff, fitMode);
			fpath.drawFitLine(graphics);
		}
		
	}
	
}